<?php

namespace App\Models\Train\Stations\Repositories;

use App\Facades\StationsGeoFacade;
use App\Models\Train\Stations\TrainStation;
use App\Trip\Repository\BaseRepository;
use Exception;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use JetBrains\PhpStorm\Pure;

class StationRepository extends BaseRepository implements Interfaces\StationRepositoryInterface
{

    #[Pure] public function __construct(TrainStation $station)
    {
        parent::__construct($station);
        $this->model = $station;
    }

    /**
     * @inheritDoc
     */
    public function listStations($condition = []): Collection
    {
        return $this->model
            // 过滤香港
            //            ->where('citycode', '!=', 1852)
            ->where(function ($q) use ($condition) {
                if (isset($condition['citycode']) && $condition['citycode']) {
                    $wh = is_array($condition['citycode']) ? 'whereIn' : 'where';
                    $q->$wh('citycode', $condition['citycode']);
                }

                if (isset($condition['pcode']) && $condition['pcode']) {
                    $wh = is_array($condition['pcode']) ? 'whereIn' : 'where';
                    $q->$wh('pcode', $condition['pcode']);
                }
            })
            ->get();
    }

    /**
     * 列出站点相关的车次
     * @inheritDoc
     */
    public function listStationNumbers($station_code): Collection
    {
//        $station = $this->findOneByCode($station_code);
    }

    public function updateStation(TrainStation $station, array $data): bool
    {
        return $this->update($data, $station);
    }

    public function findOneByName(string $station_name, bool $isThrow = false): ?TrainStation
    {
        $cache_key = "Train:StationName:{$station_name}";
        if (Cache::has($cache_key)) {
            $station = Cache::get($cache_key);
            if (!$this->isRefresh($station)) {
                return $station;
            }
        }
        if ($isThrow) {
            $station = $this->findOneByOrFail(['station_name' => $station_name]);
        } else {
            $station = $this->findOneBy(['station_name' => $station_name]);
        }

        if ($station) {
            Cache::put($cache_key, $station, 30 * config('trip.ttl.day'));
        }
        return $station;
    }

    /**
     * 类 fillable 等值是否变化 ,如果变化了 重新缓存
     * @param TrainStation $station
     * @return bool
     */
    private function isRefresh(TrainStation $station): bool
    {
        // 类基本属性是否变化
        if (count($station->getFillable()) != count($this->model->getFillable())) {
            return true;
        }
        return false;
    }

    public function listHotStations(int $limit = 6): Collection
    {
        return $this->model->where('hot', 1)
                           ->orderByDesc('sorts')
                           ->limit($limit)
                           ->get([
                               'sorts',
                               'station_name',
                               'station_name_uy',
                               'station_code',
                           ]);
    }

    public function listBy(array $condition = [], array $columns = ['*']): Collection
    {
        return $this->model
            ->when(isset($condition['citycode']), function ($q) use ($condition) {
                $q->where('citycode', $condition['citycode']);
            })
            ->when(isset($condition['adcode']), function ($q) use ($condition) {
                $q->where('adcode', $condition['adcode']);
            })
            ->where('state', 0)
            ->get($columns);
    }

    public function listHotCities(int $limit = 6): Collection
    {
        // TODO: Implement listHotCities() method.
    }

    public function addOrUpdateStations(array $stations)
    {
        foreach ($stations as $data) {
            $addData = [
                'station_name' => $data['station_name'],
                'station_code' => $data['station_code'],
                'pinyin'       => $data['pinyin'],
                'abbr'         => $data['abbr'],
                'initial'      => $data['initial'],
            ];

            if (isset($data['station_name_uy']) && $data['station_name_uy']) {
                $addData['station_name_uy'] = $data['station_name_uy'];
            }

            if (isset($data['initial_uy']) && $data['initial_uy']) {
                $addData['initial_uy'] = $data['initial_uy'];
            }

            if (isset($data['cityname']) && $data['cityname']) {
                $addData['cityname'] = $data['cityname'];
            }

            if (isset($data['pname']) && $data['pname']) {
                $addData['pname'] = $data['pname'];
            }

            $station = $this->findOneByCode($data['station_code']);
            if ($station) {
                unset($addData['station_name']);
            }
            $this->addStation($addData, true);
        }
    }

    /**
     * @throws Exception
     */
    public function findOneByCode(string $station_code, bool $isThrow = false): ?TrainStation
    {
        $cache_key = "Train:StationCode:{$station_code}";
        if (Cache::has($cache_key)) {
            $station = Cache::get($cache_key);
            if (!$this->isRefresh($station)) {
                if (!StationsGeoFacade::hasGeo($station->station_code)) {
                    StationsGeoFacade::addGeoByStation($station);
                }
                return $station;
            }
        }
        if ($isThrow) {
            try {
                $station = $this->findOneByOrFail(['station_code' => $station_code]);
            } catch (Exception $e) {
                throw  new Exception('站点不存在'.":{$station_code}", 404);
            }
        } else {
            $station = $this->findOneBy(['station_code' => $station_code]);
        }

        if ($station) {
            Cache::put($cache_key, $station, 30 * config('trip.ttl.day'));
            if (!StationsGeoFacade::hasGeo($station->station_code)) {
                StationsGeoFacade::addGeoByStation($station);
            }
        }
        return $station;
    }

    /**
     * @inheritDoc
     */
    public function addStation(array $data, bool $update = false): TrainStation
    {
        $station = $this->findOneByCode($data['station_code']);
        if ($station) {
            $update && $this->update($data, $station);
            return $station;
        }
        return $this->create($data);
    }
}